/* Global defines */
#include <stdlib.h>
#include <string.h>
#include <time.h>
/* Project defines */
#include "trace.h"
#include "tools.h"
#include "crc16.h"
#include "xelix_proto.h"

/* Local variables */

/* Local functions */

/* Global functions */

/*
fname: xelix_msg_create
desc: creates xelix message buffer from data structure
args:
    data: data structure
    buff: msg buffer output(must be allocated)
    sz:   size of allocated buffer
return:
	-x: x - error code from fsm
    -1: if unknown error occurs
    n[0:inf]: bytes written to buffer
*/
int xelix_msg_create(p_xelix_proto_t data, void *buff, int *sz){
    int ret = ERR;
    e_xelix_proto_fsm msg_create_fsm = e_xelix_fsm_error;
	uint8_t *tmp_buff = (uint8_t*)buff;

    ASSERT(data != NULL);
    ASSERT(tmp_buff != NULL);
    ASSERT(sz != NULL);

    for (ret = 0; ret <= *sz;){
		switch(msg_create_fsm){
			case e_xelix_msg_error:{
				msg_create_fsm++;
			}break;

			case e_xelix_fsm_header:{
				int field_sz = sizeof(data->msg_header);
				if (data->msg_header == 0x0){
					memcpy_inv((void*)&tmp_buff[ret], (void*)XELIX_PROTO_HEADER, field_sz);
				}
				else{
					memcpy_inv((void*)&tmp_buff[ret], (void*)&data->msg_header, field_sz);
				}
				ret+=field_sz;
				msg_create_fsm++;
			}break;

			case e_xelix_fsm_pmsg_type:{
				int field_sz = sizeof(data->msg_type);
				if (data->msg_type > e_xelix_msg_error && data->msg_type < e_xelix_msg_last){
					memcpy_inv((void*)&tmp_buff[ret], (void*)&data->msg_type, field_sz);
				}
				else{
					return ret*(-1);
				}
				ret+=field_sz;
				msg_create_fsm++;
			}break;

			case e_xelix_fsm_pmsg_seq_num:{
				int field_sz = sizeof(data->msg_seq_num);
				uint16_t msg_seq_num;
				msg_seq_num = rand() % 0xFFFE + 1;
				if (msg_seq_num > 0 && msg_seq_num < UINT16_MAX){
					data->msg_seq_num = msg_seq_num;
					memcpy_inv((void*)&tmp_buff[ret], (void*)&data->msg_seq_num, field_sz);
				}
				else{
					return ret*(-1);
				}
				ret+=field_sz;
				msg_create_fsm++;
			}break;

			case e_xelix_fsm_cont_sz:{
				int field_sz = sizeof(data->msg_sz);
				if (data->msg_sz > 0 && data->msg_sz < UINT16_MAX){
					memcpy_inv((void*)&tmp_buff[ret], (void*)&data->msg_sz, field_sz);
				}
				else{
					return ret*(-1);
				}
				ret+=field_sz;
				msg_create_fsm++;
			}break;

			case e_xelix_fsm_content:{
				int field_sz = data->msg_sz;
				memcpy_inv((void*)&tmp_buff[ret], (void*)data->msg_content, field_sz);
				ret+=field_sz;
				msg_create_fsm++;
			}break;

			case e_xelix_fsm_crc16:{
				int field_sz = sizeof(data->crc16);
				uint16_t crc16 = crc16_ccitt(tmp_buff, ret);
				data->crc16 = crc16;
				memcpy_inv((void*)&tmp_buff[ret], (void*)&data->crc16, field_sz);
				ret+=field_sz;
				msg_create_fsm=e_xelix_msg_error;
				return ret;
			}break;

			default:{
				return ret = ERR;
			}break;
		}
    }



    return ret;
}

/*
fname: xelix_msg_parse
desc: parse xelix message buffer to data structure
args:
	buff: msg buffer input
    data: data structure (content will be allocated. Free after usage).
    sz:   size of msg buffer
return:
	-x: x - error code from fsm
    -1: if unknown error occurs
    n[0:inf]: bytes read from buffer
*/
int xelix_msg_parse(void *buff, p_xelix_proto_t data, int *sz){
	int ret = ERR;
    e_xelix_proto_fsm msg_parse_fsm = e_xelix_fsm_error;
	uint8_t *tmp_buff = (uint8_t*)buff;

    ASSERT(data != NULL);
    ASSERT(tmp_buff != NULL);
    ASSERT(sz != NULL);

	for (ret = 0; ret <= *sz;){
		switch(msg_parse_fsm){
			case e_xelix_msg_error:{
				msg_parse_fsm++;
			}break;

			case e_xelix_fsm_header:{
				uint16_t header = 0x0;
				int field_sz = sizeof(data->msg_header);
				memcpy_inv((void*)&header, (void*)&tmp_buff[ret], field_sz);
				if (header == 0){
					return msg_parse_fsm*(-1);
				}
				else {
					data->msg_header = header;
				}
				ret+=field_sz;
				msg_parse_fsm++;
			}break;

			case e_xelix_fsm_pmsg_type:{
				uint16_t msg_type = 0x0;
				int field_sz = sizeof(data->msg_type);
				memcpy_inv((void*)&msg_type, (void*)&tmp_buff[ret], field_sz);
				if (msg_type > e_xelix_msg_error && msg_type < e_xelix_msg_last){
					data->msg_type = msg_type;
				}
				else{
					return msg_parse_fsm*(-1);
				}
				ret+=field_sz;
				msg_parse_fsm++;
			}break;

			case e_xelix_fsm_pmsg_seq_num:{
				uint16_t msg_seq_num = 0x0;
				int field_sz = sizeof(data->msg_seq_num);
				memcpy_inv((void*)&msg_seq_num, (void*)&tmp_buff[ret], field_sz);
				if (msg_seq_num > 0 && msg_seq_num < UINT16_MAX){
					data->msg_seq_num = msg_seq_num;
				}
				else{
					return msg_parse_fsm*(-1);
				}
				ret+=field_sz;
				msg_parse_fsm++;
			}break;

			case e_xelix_fsm_cont_sz:{
				uint16_t msg_sz = 0x0;
				int field_sz = sizeof(data->msg_sz);
				memcpy_inv((void*)&msg_sz, (void*)&tmp_buff[ret], field_sz);
				if (msg_sz > 0 && msg_sz < UINT16_MAX){
					data->msg_sz = msg_sz;
				}
				else{
					return msg_parse_fsm*(-1);
				}
				ret+=field_sz;
				msg_parse_fsm++;
			}break;

			case e_xelix_fsm_content:{
				int field_sz = data->msg_sz;
				data->msg_content = malloc(field_sz);
				memcpy_inv((void*)data->msg_content, (void*)&tmp_buff[ret], field_sz);
				ret+=field_sz;
				msg_parse_fsm++;
			}break;

			case e_xelix_fsm_crc16:{
				uint16_t crc16_read = 0x0, crc16_calc = 0x0;
				int field_sz = sizeof(data->crc16);
				memcpy_inv((void*)&crc16_read, (void*)&tmp_buff[ret], field_sz);
				crc16_calc = crc16_ccitt(tmp_buff, ret);
				if (crc16_read == crc16_calc){
					data->crc16 = crc16_read;
					ret+=field_sz;
					msg_parse_fsm++;
					return ret;
				}
				else{
					free(data->msg_content);
					return msg_parse_fsm*(-1);
				}
			}break;

			default:{
				return ret = ERR;
			}break;
		}
	}

	return ret;
}


/* End of file */
